/**
 * Copyright (C) 2021 - 2031 O-Cubes Co., Ltd.
 */

/****************************************************************
 *  @file    core_rv32.h
 *  @brief   E906 core-related drivers
 *  @version v1.0
 *  @date    03. Apr. 2023
 ****************************************************************/

#ifndef __CORE_RV32_H__
#define __CORE_RV32_H__

#include <stdint.h>

#include "io.h"
#include "rv32_gcc.h"

#ifdef __cplusplus
extern "C" {
#endif

#ifndef __RV32
#define __RV32                    (0x01U)
#endif

/** __FPU_USED indicates whether an FPU is used or not.
 *  This core does not support an FPU at all
 */
#define __FPU_USED                0U

#if defined ( __GNUC__ )
#if defined (__VFP_FP__) && !defined(__SOFTFP__)
#error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)"
#endif
#endif

/* check device defines and use defaults */
#ifndef __RV32_REV
#define __RV32_REV                0x0000U
#endif

#ifndef __VIC_PRIO_BITS
#define __VIC_PRIO_BITS           4U
#endif

#ifndef __Vendor_SysTickConfig
#define __Vendor_SysTickConfig    1U
#endif

#ifndef __MPU_PRESENT
#define __MPU_PRESENT             1U
#endif

#ifndef __ICACHE_PRESENT
#define __ICACHE_PRESENT          1U
#endif

#ifndef __DCACHE_PRESENT
#define __DCACHE_PRESENT          1U
#endif

#define MPU_REGION_NUM            8U

/**
 * Access to the structure of a vector interrupt controller.
 */
typedef struct {
	__IOM uint8_t IP;              /*!< Offset: 0x000 (R/W)  Interrupt set pending register */
	__IOM uint8_t IE;              /*!< Offset: 0x004 (R/W)  Interrupt set enable register */
	__IOM uint8_t ATTR;            /*!< Offset: 0x008 (R/W)  Interrupt set attribute register */
	__IOM uint8_t CTL;             /*!< Offset: 0x00C (R/W)  Interrupt control register */
} CLIC_INT_Control;

typedef struct {
	__IOM uint32_t CLICCFG: 8;     /*!< Offset: 0x000 (R/W)  CLIC configure register */
	__IM  uint32_t CLICINFO;
	__IOM uint32_t MINTTHRESH;
	uint32_t RESERVED[1021];
	CLIC_INT_Control CLICINT[4096]; /* Base Address, 0xE0801000 */
} CLIC_Type;

#define CLIC_INFO_CLICINTCTLBITS_Pos           21U
#define CLIC_INFO_CLICINTCTLBITS_Msk           (0xFUL << CLIC_INFO_CLICINTCTLBITS_Pos)

#define CLIC_INTIP_IP_Pos                      0U                                    /*!< CLIC INTIP: IP Position */
#define CLIC_INTIP_IP_Msk                      (0x1UL << CLIC_INTIP_IP_Pos)          /*!< CLIC INTIP: IP Mask */

#define CLIC_INTIE_IE_Pos                      0U                                    /*!< CLIC INTIE: IE Position */
#define CLIC_INTIE_IE_Msk                      (0x1UL << CLIC_INTIE_IE_Pos)          /*!< CLIC INTIE: IE Mask */

#define CLIC_INTIE_T_Pos                       7U                                    /*!< CLIC INTIE: T Position */
#define CLIC_INTIE_T_Msk                       (0x1UL << CLIC_INTIE_T_Pos)           /*!< CLIC INTIE: T Mask */

#define CLIC_INTATTR_TRIG_Pos                  1U                                    /*!< CLIC INTATTR: TRIG Position */
#define CLIC_INTATTR_TRIG_Msk                  (0x3UL << CLIC_INTATTR_TRIG_Pos)      /*!< CLIC INTATTR: TRIG Mask */

#define CLIC_INTATTR_SHV_Pos                   0U                                    /*!< CLIC INTATTR: SHV Position */
#define CLIC_INTATTR_SHV_Msk                   (0x1UL << CLIC_INTATTR_SHV_Pos)       /*!< CLIC INTATTR: SHV Mask */

#define CLIC_INTCFG_NVBIT_Pos                  5U                                    /*!< CLIC INTCFG: NVBIT Position */
#define CLIC_INTCFG_NVBIT_Msk                  (0x1UL << CLIC_INTCFG_NVBIT_Pos)      /*!< CLIC INTCFG: NVBIT Mask */

#define CLIC_INTCFG_PRIO_Pos                   5U                                    /*!< CLIC INTCFG: INTCFG Position */
#define CLIC_INTCFG_PRIO_Msk                   (0x7UL << CLIC_INTCFG_PRIO_Pos)       /*!< CLIC INTCFG: INTCFG Mask */

#define CLIC_CLICCFG_NVBIT_Pos                 0U                                    /*!< CLIC CLICCFG: NVBIT Position */
#define CLIC_CLICCFG_NVBIT_Msk                 (0x1UL << CLIC_CLICCFG_NVBIT_Pos)     /*!< CLIC CLICCFG: NVBIT Mask */

#define CLIC_CLICCFG_NLBIT_Pos                 1U                                    /*!< CLIC CLICCFG: NLBIT Position */
#define CLIC_CLICCFG_NLBIT_Msk                 (0xFUL << CLIC_CLICCFG_NLBIT_Pos)     /*!< CLIC CLICCFG: NLBIT Mask */

#define CLIC_CLICCFG_NMBIT_Pos                 5U                                    /*!< CLIC CLICCFG: NMBIT Position */
#define CLIC_CLICCFG_NMBIT_Msk                 (0x3UL << CLIC_CLICCFG_NMBIT_Pos)     /*!< CLIC CLICCFG: NMBIT Mask */


/**
 * Type definitions for the PMP Registers
 */
#define PMP_PMPCFG_R_Pos                       0U                                    /*!< PMP PMPCFG: R Position */
#define PMP_PMPCFG_R_Msk                       (0x1UL << PMP_PMPCFG_R_Pos)           /*!< PMP PMPCFG: R Mask */

#define PMP_PMPCFG_W_Pos                       1U                                    /*!< PMP PMPCFG: W Position */
#define PMP_PMPCFG_W_Msk                       (0x1UL << PMP_PMPCFG_W_Pos)           /*!< PMP PMPCFG: W Mask */

#define PMP_PMPCFG_X_Pos                       2U                                    /*!< PMP PMPCFG: X Position */
#define PMP_PMPCFG_X_Msk                       (0x1UL << PMP_PMPCFG_X_Pos)           /*!< PMP PMPCFG: X Mask */

#define PMP_PMPCFG_A_Pos                       3U                                    /*!< PMP PMPCFG: A Position */
#define PMP_PMPCFG_A_Msk                       (0x3UL << PMP_PMPCFG_A_Pos)           /*!< PMP PMPCFG: A Mask */

#define PMP_PMPCFG_L_Pos                       7U                                    /*!< PMP PMPCFG: L Position */
#define PMP_PMPCFG_L_Msk                       (0x1UL << PMP_PMPCFG_L_Pos)           /*!< PMP PMPCFG: L Mask */

typedef enum {
	REGION_SIZE_4B       = -1,
	REGION_SIZE_8B       = 0,
	REGION_SIZE_16B      = 1,
	REGION_SIZE_32B      = 2,
	REGION_SIZE_64B      = 3,
	REGION_SIZE_128B     = 4,
	REGION_SIZE_256B     = 5,
	REGION_SIZE_512B     = 6,
	REGION_SIZE_1KB      = 7,
	REGION_SIZE_2KB      = 8,
	REGION_SIZE_4KB      = 9,
	REGION_SIZE_8KB      = 10,
	REGION_SIZE_16KB     = 11,
	REGION_SIZE_32KB     = 12,
	REGION_SIZE_64KB     = 13,
	REGION_SIZE_128KB    = 14,
	REGION_SIZE_256KB    = 15,
	REGION_SIZE_512KB    = 16,
	REGION_SIZE_1MB      = 17,
	REGION_SIZE_2MB      = 18,
	REGION_SIZE_4MB      = 19,
	REGION_SIZE_8MB      = 20,
	REGION_SIZE_16MB     = 21,
	REGION_SIZE_32MB     = 22,
	REGION_SIZE_64MB     = 23,
	REGION_SIZE_128MB    = 24,
	REGION_SIZE_256MB    = 25,
	REGION_SIZE_512MB    = 26,
	REGION_SIZE_1GB      = 27,
	REGION_SIZE_2GB      = 28,
	REGION_SIZE_4GB      = 29,
	REGION_SIZE_8GB      = 30,
	REGION_SIZE_16GB     = 31
} region_size_e;

typedef enum {
	ADDRESS_MATCHING_TOR   = 1,
	ADDRESS_MATCHING_NAPOT = 3
} address_matching_e;

typedef struct {
	uint32_t r: 1;           /* readable enable */
	uint32_t w: 1;           /* writeable enable */
	uint32_t x: 1;           /* execable enable */
	address_matching_e a: 2; /* address matching mode */
	uint32_t reserved: 2;    /* reserved */
	uint32_t l: 1;           /* lock enable */
} mpu_region_attr_t;

/* CACHE Register Definitions */
#define CACHE_MHCR_L0BTB_Pos                   12U                                           /*!< CACHE MHCR: L0BTB Position */
#define CACHE_MHCR_L0BTB_Msk                   (0x1UL << CACHE_MHCR_L0BTB_Pos)               /*!< CACHE MHCR: WA Mask */

#define CACHE_MHCR_BPE_Pos                     5U                                            /*!< CACHE MHCR: BPE Position */
#define CACHE_MHCR_BPE_Msk                     (0x1UL << CACHE_MHCR_BPE_Pos)                 /*!< CACHE MHCR: BPE Mask */

#define CACHE_MHCR_RS_Pos                      4U                                            /*!< CACHE MHCR: RS Position */
#define CACHE_MHCR_RS_Msk                      (0x1UL << CACHE_MHCR_RS_Pos)                  /*!< CACHE MHCR: RS Mask */

#define CACHE_MHCR_WA_Pos                      3U                                            /*!< CACHE MHCR: WA Position */
#define CACHE_MHCR_WA_Msk                      (0x1UL << CACHE_MHCR_WA_Pos)                  /*!< CACHE MHCR: WA Mask */

#define CACHE_MHCR_WB_Pos                      2U                                            /*!< CACHE MHCR: WB Position */
#define CACHE_MHCR_WB_Msk                      (0x1UL << CACHE_MHCR_WB_Pos)                  /*!< CACHE MHCR: WB Mask */

#define CACHE_MHCR_DE_Pos                      1U                                            /*!< CACHE MHCR: DE Position */
#define CACHE_MHCR_DE_Msk                      (0x1UL << CACHE_MHCR_DE_Pos)                  /*!< CACHE MHCR: DE Mask */

#define CACHE_MHCR_IE_Pos                      0U                                            /*!< CACHE MHCR: IE Position */
#define CACHE_MHCR_IE_Msk                      (0x1UL << CACHE_MHCR_IE_Pos)                  /*!< CACHE MHCR: IE Mask */

#define CACHE_INV_ADDR_Pos                     5U
#define CACHE_INV_ADDR_Msk                     (0xFFFFFFFFUL << CACHE_INV_ADDR_Pos)

/**
 * The data structure of the access system timer.
 */
typedef struct {
	__IOM uint32_t MTIMECMPLO;            /*!< Offset: 0x000 (R/W) Timer compare low 32-bits register */
	__IOM uint32_t MTIMECMPHI;            /*!< Offset: 0x004 (R/W) Timer compare high 32-bits register */
	uint32_t RESERVED[8188];
	__IM  uint32_t MTIMELO;               /*!< Offset: 0x7FF8 (R)  Timer current low 32-bits register */
	__IM  uint32_t MTIMEHI;               /*!< Offset: 0x7FFC (R)  Timer current high 32-bits register */
} CORET_Type;

/**
 * Mask and shift a bit field value for use in a register bit range.
 */
#define _VAL2FLD(field, value)    ((value << field ## _Pos) & field ## _Msk)

/**
 * Mask and shift a register value to extract a bit filed value.
 */
#define _FLD2VAL(field, value)    ((value & field ## _Msk) >> field ## _Pos)

/* Memory mapping of EOC Hardware */
#define TCIP_BASE                 (0xE0000000UL)                            /*!< Titly Coupled IP Base Address */
#define CORET_BASE                (0xE0004000UL)                            /*!< CORET Base Address */
#define CLIC_BASE                 (0xE0800000UL)                            /*!< CLIC Base Address */

#define CORET                     ((CORET_Type*)CORET_BASE)                 /*!< SysTick configuration struct */
#define CLIC                      ((CLIC_Type*)CLIC_BASE)                   /*!< CLIC configuration struct */

/* The following MACROS handle generation of the register offset and byte masks */
#define _BIT_SHIFT(IRQn)          (((((uint32_t)(int32_t)(IRQn))) & 0x03UL) * 8UL)
#define _IP_IDX(IRQn)             ((((uint32_t)(int32_t)(IRQn))  >> 5UL))
#define _IP2_IDX(IRQn)            ((((uint32_t)(int32_t)(IRQn))  >> 2UL))

/**
 * Enable External Interrupt
 */
__STATIC_INLINE void clic_enable_irq(int32_t IRQn)
{
	CLIC->CLICINT[IRQn].IE |= CLIC_INTIE_IE_Msk;
}

/**
 * Disable External Interrupt
 */
__STATIC_INLINE void clic_disable_irq(int32_t IRQn)
{
	CLIC->CLICINT[IRQn].IE &= ~CLIC_INTIE_IE_Msk;
}

/**
 * Enable External Secure Interrupt
 */
__STATIC_INLINE void clic_enable_sirq(int32_t IRQn)
{
	CLIC->CLICINT[IRQn].IE |= (CLIC_INTIE_IE_Msk | CLIC_INTIE_T_Msk);
}

/**
 * Disable External Secure Interrupt
 */
__STATIC_INLINE void clic_disable_sirq(int32_t IRQn)
{
	CLIC->CLICINT[IRQn].IE &= ~(CLIC_INTIE_IE_Msk | CLIC_INTIE_T_Msk);
}

/**
 * Check Interrupt is Enabled or not
 */
__STATIC_INLINE uint32_t clic_get_enabled_irq(int32_t IRQn)
{
	return (uint32_t)(CLIC->CLICINT[IRQn].IE & CLIC_INTIE_IE_Msk);
}

/**
 * Check Interrupt is Pending or not
 */
__STATIC_INLINE uint32_t clic_get_pending_irq(int32_t IRQn)
{
	return (uint32_t)(CLIC->CLICINT[IRQn].IP & CLIC_INTIP_IP_Msk);
}

/**
 * Set Pending Interrupt
 */
__STATIC_INLINE void clic_set_pending_irq(int32_t IRQn)
{
	CLIC->CLICINT[IRQn].IP |= CLIC_INTIP_IP_Msk;
}

/**
 * Clear Pending Interrupt
 */
__STATIC_INLINE void clic_clear_pending_irq(int32_t IRQn)
{
	CLIC->CLICINT[IRQn].IP &= ~CLIC_INTIP_IP_Msk;
}

/**
 * Set the priority of an interrupt.
 * The priority cannot be set for every core interrupt.
 */
__STATIC_INLINE void clic_set_prio(int32_t IRQn, uint32_t priority)
{
	uint8_t val = 0xFFU;
	uint8_t nlbits = (CLIC->CLICINFO & CLIC_INFO_CLICINTCTLBITS_Msk) >> CLIC_INFO_CLICINTCTLBITS_Pos;
	CLIC->CLICINT[IRQn].CTL = (CLIC->CLICINT[IRQn].CTL & (~val)) | ((priority << (8 - nlbits)) & 0xFFU);
}

/**
 * Read the priority of an interrupt.
 * The interrupt number can be positive to specify an external (device specific) interrupt,
 * or negative to specify an internal (core) interrupt.
 * Value is aligned automatically to the implemented priority bits of the microcontroller.
 */
__STATIC_INLINE uint32_t clic_get_prio(int32_t IRQn)
{
	uint8_t nlbits = (CLIC->CLICINFO & CLIC_INFO_CLICINTCTLBITS_Msk) >> CLIC_INFO_CLICINTCTLBITS_Pos;
	return CLIC->CLICINT[IRQn].CTL >> (8 - nlbits);
}

/**
 * Set the interrupt handler according to the interrupt num, the handler will be filled in irq vectors.
 */
__STATIC_INLINE void clic_set_vector(int32_t IRQn, uint32_t handler)
{
	if (IRQn >= 0 && IRQn < 1024) {
		uint32_t *vectors = (uint32_t *)__get_MTVT();
		vectors[IRQn] = handler;
	}
}

/**
 * Get the address of interrupt handler function.
 */
__STATIC_INLINE uint32_t clic_get_vector(int32_t IRQn)
{
	if (IRQn >= 0 && IRQn < 1024) {
		uint32_t *vectors = (uint32_t *)__get_MTVT();
		return (uint32_t)vectors[IRQn];
	}

	return 0;
}

/**
  \brief  configure memory protected region.
  \details
  \param [in]  idx        memory protected region (0, 1, 2, ..., MPU_REGION_NUM).
  \param [in]  base_addr  base address must be aligned with page size.
  \param [in]  size       \ref region_size_e. memory protected region size.
  \param [in]  attr       \ref mpu_region_attr_t. memory protected region attribute.
  \param [in]  enable     enable or disable memory protected region.
  */
__STATIC_INLINE void csi_mpu_config_region(uint32_t idx, uint32_t base_addr, region_size_e size,
				mpu_region_attr_t attr, uint32_t enable)
{
	uint8_t  pmpxcfg = 0;
	uint32_t addr = 0;

	if (idx > MPU_REGION_NUM) {
		return;
	}

	if (!enable) {
		attr.a = 0;
	}

	if (attr.a == ADDRESS_MATCHING_TOR) {
		addr = base_addr >> 2;
	} else {
		if (size == REGION_SIZE_4B) {
			addr = base_addr >> 2;
			attr.a = 2;
		} else {
			addr = ((base_addr >> 2) & (0xFFFFFFFFU - ((1 << (size + 1)) - 1))) | ((1 << size) - 1);
		}
	}

	__set_PMPADDRx(idx, addr);

	pmpxcfg |= (attr.r << PMP_PMPCFG_R_Pos) | (attr.w << PMP_PMPCFG_W_Pos) |
			(attr.x << PMP_PMPCFG_X_Pos) | (attr.a << PMP_PMPCFG_A_Pos) |
			(attr.l << PMP_PMPCFG_L_Pos);

	__set_PMPxCFG(idx, pmpxcfg);
}

/**
 * disable mpu region by idx.
 */
__STATIC_INLINE void csi_mpu_disable_region(uint32_t idx)
{
	__set_PMPxCFG(idx, __get_PMPxCFG(idx) & (~PMP_PMPCFG_A_Msk));
}

/**
  \brief   CORE timer Configuration
  \details Initializes the System Timer and its interrupt, and starts the System Tick Timer.
           Counter is in free running mode to generate periodic interrupts.
  \param [in]  ticks  Number of ticks between two interrupts.
  \param [in]  IRQn   core timer Interrupt number.
  \return          0  Function succeeded.
  \return          1  Function failed.
  \note    When the variable <b>__Vendor_SysTickConfig</b> is set to 1, then the
           function <b>SysTick_Config</b> is not included. In this case, the file <b><i>device</i>.h</b>
           must contain a vendor-specific implementation of this function.
 */
__STATIC_INLINE uint32_t coret_config(uint32_t ticks, int32_t IRQn)
{
	if (CORET->MTIMECMPLO || CORET->MTIMECMPHI) {
		CORET->MTIMECMPLO = CORET->MTIMECMPLO + ticks;
	} else {
		CORET->MTIMECMPLO = CORET->MTIMELO + ticks;
		CORET->MTIMECMPHI = CORET->MTIMEHI;
	}
	if (CORET->MTIMECMPLO < ticks) {
		CORET->MTIMECMPHI++;
	}

	return (0UL);
}

/**
 * get CORE timer reload value
 */
__STATIC_INLINE uint32_t coret_get_load(void)
{
	return CORET->MTIMECMPLO;
}

/**
 * get CORE timer reload high value
 */
__STATIC_INLINE uint32_t coret_get_loadh(void)
{
	return CORET->MTIMECMPHI;
}

/**
 * get CORE timer counter value
 */
__STATIC_INLINE uint32_t coret_get_value(void)
{
	return CORET->MTIMELO;
}

/**
 * get CORE timer counter high value
 */
__STATIC_INLINE uint32_t coret_get_valueh(void)
{
	return CORET->MTIMEHI;
}

/**
 * clean CORE timer interrupt flag
 */
__STATIC_INLINE void coret_clear_irq(void)
{
}

/**
 * Enable I-Cache
 */
__STATIC_INLINE void icache_enable(void)
{
#if (__ICACHE_PRESENT == 1U)
	uint32_t cache;
	__DSB();
	__ISB();
	__ICACHE_IALL();
	cache = __get_MHCR();
	cache |= CACHE_MHCR_IE_Msk;
	__set_MHCR(cache);
	__DSB();
	__ISB();
#endif
}


/**
 * Disable I-Cache
 */
__STATIC_INLINE void icache_disable(void)
{
#if (__ICACHE_PRESENT == 1U)
	uint32_t cache;
	__DSB();
	__ISB();
	cache = __get_MHCR();
	cache &= ~CACHE_MHCR_IE_Msk;       /* disable icache */
	__set_MHCR(cache);
	__ICACHE_IALL();                   /* invalidate all icache */
	__DSB();
	__ISB();
#endif
}


/**
 * Invalidate I-Cache
 */
__STATIC_INLINE void icache_invalid(void)
{
#if (__ICACHE_PRESENT == 1U)
	__DSB();
	__ISB();
	__ICACHE_IALL();                   /* invalidate all icache */
	__DSB();
	__ISB();
#endif
}

/**
 * Enable D-Cache
 * I-Cache also turns on.
 */
__STATIC_INLINE void dcache_enable(void)
{
#if (__DCACHE_PRESENT == 1U)
	uint32_t cache;
	__DSB();
	__ISB();
	__DCACHE_IALL();                   /* invalidate all dcache */
	cache = __get_MHCR();
	cache |= (CACHE_MHCR_DE_Msk | CACHE_MHCR_WB_Msk | CACHE_MHCR_WA_Msk | CACHE_MHCR_RS_Msk | CACHE_MHCR_BPE_Msk | CACHE_MHCR_L0BTB_Msk);      /* enable all Cache */
	__set_MHCR(cache);

	__DSB();
	__ISB();
#endif
}


/**
 * Turns off D-Cache
 * I-Cache also turns off.
 */
__STATIC_INLINE void dcache_disable(void)
{
#if (__DCACHE_PRESENT == 1U)
	uint32_t cache;
	__DSB();
	__ISB();
	cache = __get_MHCR();
	cache &= ~(uint32_t)CACHE_MHCR_DE_Msk;    /* disable all Cache */
	__set_MHCR(cache);
	__DCACHE_IALL();                          /* invalidate all Cache */
	__DSB();
	__ISB();
#endif
}


/**
 * Invalidate D-Cache
 * I-Cache also invalid
 */
__STATIC_INLINE void dcache_invalid(void)
{
#if (__DCACHE_PRESENT == 1U)
	__DSB();
	__ISB();
	__DCACHE_IALL();                         /* invalidate all Cache */
	__DSB();
	__ISB();
#endif
}


/**
 * Clean D-Cache
 * I-Cache also cleans
 */
__STATIC_INLINE void dcache_clean(void)
{
#if (__DCACHE_PRESENT == 1U)
	__DSB();
	__ISB();
	__DCACHE_CALL();                         /* clean all Cache */
	__DSB();
	__ISB();
#endif
}


/**
 * Cleans and Invalidates D-Cache
 * I-Cache also flush.
 */
__STATIC_INLINE void dcache_clean_invalid(void)
{
#if (__DCACHE_PRESENT == 1U)
	__DSB();
	__ISB();
	__DCACHE_CIALL();                        /* clean and inv all Cache */
	__DSB();
	__ISB();
#endif
}


/**
 * Invalidates D-Cache for the given address
 * param[in]   addr    address (aligned to 32-byte boundary)
 * param[in]   dsize   size of memory block (in number of bytes)
 */
__STATIC_INLINE void dcache_invalid_range(uint32_t *addr, int32_t dsize)
{
#if (__DCACHE_PRESENT == 1U)
	int32_t op_size = dsize + (uint32_t)addr % 32;
	uint32_t op_addr = (uint32_t)addr;
	int32_t linesize = 32;

	__DSB();

	while (op_size > 0) {
		__DCACHE_IPA(op_addr);
		op_addr += linesize;
		op_size -= linesize;
	}

	__DSB();
	__ISB();
#endif
}


/**
 * Cleans D-Cache for the given address
 * param[in]   addr    address (aligned to 32-byte boundary)
 * param[in]   dsize   size of memory block (in number of bytes)
 */
__STATIC_INLINE void dcache_clean_range(uint32_t *addr, int32_t dsize)
{
#if (__DCACHE_PRESENT == 1)
	int32_t op_size = dsize + (uint32_t)addr % 32;
	uint32_t op_addr = (uint32_t) addr & CACHE_INV_ADDR_Msk;
	int32_t linesize = 32;

	__DSB();

	while (op_size > 0) {
		__DCACHE_CPA(op_addr);
		op_addr += linesize;
		op_size -= linesize;
	}

	__DSB();
	__ISB();
#endif
}


/**
 * Cleans and invalidates D_Cache for the given address
 * param[in]   addr    address (aligned to 16-byte boundary)
 * param[in]   dsize   size of memory block (aligned to 16-byte boundary)
 */
__STATIC_INLINE void dcache_clean_invalid_range(uint32_t *addr, int32_t dsize)
{
#if (__DCACHE_PRESENT == 1U)
	int32_t op_size = dsize + (uint32_t)addr % 32;
	uint32_t op_addr = (uint32_t) addr;
	int32_t linesize = 32;

	__DSB();

	while (op_size > 0) {
		__DCACHE_CIPA(op_addr);
		op_addr += linesize;
		op_size -= linesize;
	}

	__DSB();
	__ISB();
#endif
}

/**
 * save the psr result before disable irq.
 */
__STATIC_INLINE uint32_t irq_save(void)
{
	uint32_t result;
	result = __get_MSTATUS();
	__disable_irq();
	return(result);
}

/**
 * restore saved primask state.
 * param [in]      irq_state  psr irq state.
 */
__STATIC_INLINE void csi_irq_restore(uint32_t irq_state)
{
	__set_MSTATUS(irq_state);
}

#ifdef __cplusplus
}
#endif

#endif /* __CORE_RV32_H__ */

